-- Copyright (c) 2025 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

-- Description: Independent VRD管理对象，管理一相同UID的VRD芯片

local log = require 'mc.logging'
local mdb = require 'mc.mdb'
local context = require 'mc.context'
local cmn = require 'common'
local defs = require 'independent_vrd.ind_vrd_defs'

local FWINVENTORY_PATH = '/bmc/kepler/UpdateService/FirmwareInventory'
local FWINVENTORY_INTERFACE = 'bmc.kepler.UpdateService.FirmwareInventory'
local FWINFO_INTERFACE = 'bmc.kepler.UpdateService.FirmwareInfo'

local ind_vrd_manager = {}
ind_vrd_manager.__index = ind_vrd_manager

--- 创建VRD对象
--- @param uid string VRD的唯一标识
--- @param board_type string 板卡类型
--- @param software_id string 软件ID
--- @return table VRD对象
function ind_vrd_manager.new(uid, board_type, software_id)
    local obj = {
        UID = uid,
        BoardType = board_type,
        SoftwareId = software_id or '',
        vrd_chips = {},                 -- 存储所有芯片对象，key为VrdId
        fw_inventory_registered = false, -- 固件清单是否已注册
        objects_complete = false         -- 对象是否获取完成
    }
    return setmetatable(obj, ind_vrd_manager)
end

--- 添加芯片对象到中
--- @param vrd_id number VRD芯片ID（从1递增）
--- @param chip_obj table 芯片对象（如sd500x实例）
--- @return boolean 添加是否成功
function ind_vrd_manager:add_chip(vrd_id, chip_obj)
    if not vrd_id or not chip_obj then
        log:error('[IndVrdManager] Invalid vrd_id or chip_obj')
        return false
    end

    self.vrd_chips[vrd_id] = chip_obj
    log:debug('[IndVrdManager] Added chip with VrdId=%d to UID=%s', vrd_id, self.UID)
    return true
end

--- 获取芯片数量
--- @return number 芯片数量
function ind_vrd_manager:get_chip_count()
    local count = 0
    for _, _ in pairs(self.vrd_chips) do
        count = count + 1
    end
    return count
end

--- 获取拼接的版本号（按VrdId从小到大排序，用"."拼接）
--- @return string 拼接后的版本号
function ind_vrd_manager:get_version()
    local vrd_ids = {}
    for vrd_id, _ in pairs(self.vrd_chips) do
        table.insert(vrd_ids, vrd_id)
    end

    -- 排序VrdId
    table.sort(vrd_ids)

    -- 拼接版本号
    local versions = {}
    for _, vrd_id in ipairs(vrd_ids) do
        local chip_obj = self.vrd_chips[vrd_id]
        local ver = chip_obj:get_version()
        if ver and ver ~= '' then
            table.insert(versions, ver)
        else
            table.insert(versions, '') -- 空版本使用空字符串
        end
    end

    local version_str = table.concat(versions, '.')
    log:debug('[IndVrdManager] UID=%s version=%s', self.UID, version_str)
    return version_str
end

--- 注册固件清单信息
--- @param bus table 总线对象
function ind_vrd_manager:register_firmware_info(bus)
    if self.fw_inventory_registered then
        log:debug('[IndVrdManager] UID=%s firmware info already registered', self.UID)
        return
    end

    cmn.skynet.fork(function()
        local ok, obj = pcall(mdb.get_object, bus, FWINVENTORY_PATH, FWINVENTORY_INTERFACE)
        local times = 120 -- 重试120s, 防止资源树未加载
        while not ok and times > 0 do
            ok, obj = pcall(mdb.get_object, bus, FWINVENTORY_PATH, FWINVENTORY_INTERFACE)
            cmn.skynet.sleep(100)
            times = times - 1
        end
        if not ok then
            log:error('[IndVrdManager] UID=%s Get FirmwareInventory object failed. err: %s', self.UID, obj)
            return
        end

        local version = self:get_version()
        if version == '' then
            log:warn('[IndVrdManager] UID=%s version is empty, skip register', self.UID)
            return
        end

        local param = {
            Id = 'VRD_' .. self.BoardType .. '_' .. self.UID,
            Name = 'VRD_' .. self.BoardType,
            Version = version,
            BuildNum = '',
            ReleaseDate = '',
            LowestSupportedVersion = '',
            SoftwareId = self.SoftwareId,
            Manufacturer = '',
            Location = '',
            State = 'Enabled',
            Severity = 'Informational'
        }
        obj:Add(context.new(), param, true, 1, 90)

        self.fw_inventory_registered = true
        log:notice('[IndVrdManager] UID=%s register firmware info success, SoftwareId=%s, Version=%s',
            self.UID, self.SoftwareId, version)
    end)
end

--- 更新固件清单版本信息
--- @param bus table 总线对象
function ind_vrd_manager:update_firmware_version(bus)
    local version = self:get_version()
    local firmware_id = 'VRD_' .. self.BoardType .. '_' .. self.UID
    local firmware_path = FWINVENTORY_PATH .. '/' .. firmware_id

    cmn.skynet.fork(function()
        local ok, obj = pcall(mdb.get_cached_object, bus, firmware_path, FWINFO_INTERFACE)
        if not ok then
            log:error('[IndVrdManager] UID=%s Get FirmwareInfo object failed. err: %s', self.UID, obj)
            return
        end

        log:info('[IndVrdManager] UID=%s update firmware version to %s', self.UID, version)
        obj.Version = version
    end)
end

--- 执行升级操作
--- @param dir string 升级文件目录
--- @param hpm_path string hpm路径
--- @param progress_callback function|nil 进度回调函数 function(chip_index, chip_percentage)
--- @return number 升级结果码
function ind_vrd_manager:upgrade(dir, hpm_path, progress_callback)
    log:notice('[IndVrdManager] Start upgrade for UID=%s', self.UID)

    local vrd_ids = {}
    for vrd_id, _ in pairs(self.vrd_chips) do
        table.insert(vrd_ids, vrd_id)
    end
    table.sort(vrd_ids)

    local total_chips = #vrd_ids
    if total_chips == 0 then
        log:error('[IndVrdManager] No chips to upgrade for UID=%s', self.UID)
        return defs.RET.ERR
    end

    -- 依次升级每个芯片
    for idx, vrd_id in ipairs(vrd_ids) do
        local chip_obj = self.vrd_chips[vrd_id]
        log:notice('[IndVrdManager] Upgrading chip %d/%d, VrdId=%d', idx, total_chips, vrd_id)

        -- 创建芯片进度回调，传递芯片索引和进度百分比
        local chip_progress_callback
        if progress_callback then
            chip_progress_callback = function(chip_percent)
                -- 回调时传递芯片索引和当前芯片的进度
                progress_callback(idx, chip_percent)
            end
        end

        local ret = chip_obj:upgrade(dir, hpm_path, chip_progress_callback)
        if ret ~= defs.RET.OK then
            log:error('[IndVrdManager] Chip VrdId=%d upgrade failed', vrd_id)
            return defs.RET.ERR
        end

        -- 确保当前芯片进度达到100%
        if progress_callback then
            progress_callback(idx, 100)
        end
    end

    log:notice('[IndVrdManager] Upgrade completed for UID=%s', self.UID)
    return defs.RET.OK
end

--- 设置对象获取完成标志
--- @param complete boolean 是否完成
function ind_vrd_manager:set_objects_complete(complete)
    self.objects_complete = complete
    log:debug('[IndVrdManager] UID=%s objects_complete set to %s', self.UID, tostring(complete))
end

--- 获取对象获取完成标志
--- @return boolean 是否完成
function ind_vrd_manager:is_objects_complete()
    return self.objects_complete
end

return ind_vrd_manager
